package pl.marekbar;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.drawable.BitmapDrawable;
import android.util.Log;


/**
 * @author marekbar1985<br><br>
 * {@literal This is enhanced version of ImageManager from PseudoBlue blog}
 * @since 2012
 * @version 1.0
 * @see <a href="https://sites.google.com/site/napiszprogrampl/">Napisz program</a>
 * @see <a href="83.15.3.68">SKN Informatyki PWSTE</a>
* @see <a href="http://blog.pseudoblue.com/2010/08/15/android-bitmaps-and-memory-leaks/">
Inspired by PseudoBlue - "Android Bitmaps and Memory Leaks"</a>
 */
public class BitmapGC {

	private String TAG;
	private HashMap<Integer,Bitmap> bitmapsWithIntegerID;
	private HashMap<String,Integer> names;
	private Context context;
	private int lastID;
	private int genID = 0;;

	/**
	 * Bitmap manager constructor
	 * @param context - application context
	 */
	public BitmapGC(Context context){
		this.context = context;
		bitmapsWithIntegerID = new HashMap<Integer,Bitmap>();
		names = new HashMap<String,Integer>();
		TAG = context.getClass().getName();
		lastID = 0;
		genID = 0;
	}

	/**
	 * You may want to reuse your Bitmap but you should keep
	 * recently add Bitmap ID
	 * @return Bitmap ID
	 */
	public int getLastID(){
		return lastID;
	}
	
	/**
	 * Generate ID for added Bitmap if not specified
	 * @return Bitmap ID
	 */
	private int generateID(){
		genID++;
		lastID = genID;
		return genID;
	}
	
	
	/**
	 * You can assign name to previously added Bitmap into
	 * this tracking object - BitmapGC.
	 * @param name of your choice
	 */
	public void setNameForLastAddedBitmap(String name){
		if(!names.containsKey(name))
			names.put(name, lastID);
	}
	
	/**
	 * Get Bitmap from this container by text name
	 * @param bitmapGivenName - bitmap name in this container
	 * @return Bitmap
	 */
	public Bitmap getBitmapWhichNameIs(String bitmapGivenName){
		if(names.containsKey(bitmapGivenName))
			return this.bitmapsWithIntegerID.get(names.get(bitmapGivenName));
		else
			return null;
	}
	
	/**
	 * Removes Bitmap controlled by this bitmap manager
	 * @param index - bitmap ID
	 */
	public void removeAt(int index){
		this.bitmapsWithIntegerID.get(index).recycle();
		this.bitmapsWithIntegerID.remove(index);
		Iterator<Entry<String, Integer>> it = names.entrySet().iterator();
		while(it.hasNext()){
			Map.Entry<String,Integer> entry = it.next();
			if(entry.getValue() == index){
				names.remove(entry.getValue());
				break;
			}
		
		}
	}
	/**
	 * Destroyes all controlled bitmaps and releases memory on the native heap.
	 */
	public void gc(){
		Iterator<Entry<Integer, Bitmap>> iterator = 
				this.bitmapsWithIntegerID.entrySet().iterator();
		while(iterator.hasNext()){
			Map.Entry<Integer,Bitmap> entry = iterator.next();
			entry.getValue().recycle();
		}
		this.bitmapsWithIntegerID.clear();
		this.bitmapsWithIntegerID = null;
		
		this.names.clear();
		
		Runtime.getRuntime().gc();
	}
	/**
	 * Takes over Bitmap - writes its address so it can be freed later.
	 * Can't be reused later. One use.
	 * @param bitmap
	 * @return Bitmap - same as input Bitmap
	 */
	public Bitmap takeOverBitmap(Bitmap bitmap){
		if(bitmap == null){
			log("Bitmap is null - I don't add such bitmaps");
			return null;
		}
		if(!bitmapsWithIntegerID.containsKey(generateID())){
			bitmapsWithIntegerID.put(lastID, bitmap);
		}
		return bitmapsWithIntegerID.get(lastID);
	}
	
	
	
	public Bitmap takeOverBitmap(String sciezkaPliku)
	{
		File plik = new File(sciezkaPliku);
		if (! plik.exists()) return null;
		
		BitmapDrawable bitmapaSurowa = new BitmapDrawable(sciezkaPliku);
		Bitmap bitmapa = bitmapaSurowa.getBitmap();
		
		if(!bitmapsWithIntegerID.containsKey(generateID())){
			bitmapsWithIntegerID.put(lastID, bitmapa);
		}
		return bitmapsWithIntegerID.get(lastID);
	}
	
	/**
	 * Takes over Bitmap control - bitmap can be used many times
	 * @param Bitmap
	 * @param ID of your choice
	 * @return Bitmap same as input Bitmap
	 */
	public Bitmap takeOverBitmap(Bitmap bitmap,int ID){
		if(bitmap == null){
			log("Bitmap is null - I don't add such bitmaps");
			return null;
		}
		lastID = ID;
		if(!bitmapsWithIntegerID.containsKey(ID)){
			bitmapsWithIntegerID.put(ID, bitmap);
		}
		return bitmapsWithIntegerID.get(ID);
	}
	
	/**
	 * Takes over Bitmap control created from drawable resource.
	 * @param bitmapFromDrawable - ID of drawable item and its ID in this container
	 * @return Bitmap created from drawable.
	 */
	public Bitmap takeOverDrawable(int bitmapFromDrawable){
		lastID = generateID();
		if(!bitmapsWithIntegerID.containsKey(lastID)){
			bitmapsWithIntegerID.put(lastID,
					BitmapFactory.decodeResource(
							context.getResources(),
							bitmapFromDrawable));
		}
		return bitmapsWithIntegerID.get(lastID);
	}
	
	
	/**
	 * Takes over Bitmap control from asset file.
	 * One time use.
	 * @param bitmapFromAsset - asset bitmap filename
	 * @return Bitmap from asset
	 */
	public Bitmap takeOverAsset(String bitmapFromAsset){
		try{
	        AssetManager assetManager = context.getAssets();
	        InputStream stream = assetManager.open(bitmapFromAsset);
	        int ID = this.generateID();
			if(!bitmapsWithIntegerID.containsKey(ID)){
				bitmapsWithIntegerID.put(ID,BitmapFactory.decodeStream(stream));
			}
			return bitmapsWithIntegerID.get(ID);
	    } catch (IOException e) {
			err("Can't open this file: "+ bitmapFromAsset + " which may not exist in assets");
	    	return null;
		}
	}
	/**
	 * Returns bitmap from this container
	 * @param ID of bitmap
	 * @return Bitmap from specified ID at this container
	 */
	public Bitmap getBitmapWithID(int ID){
		if(bitmapsWithIntegerID.containsKey(ID)){
			return bitmapsWithIntegerID.get(ID);
		}else{
			err("Bitmap with ID = " + ID + " doesn't exist in this container");
			return null;
		}
	}
	
	/**
	 * Mix two bitmaps and create one from them.
	 * @param backgroundBitmap 
	 * @param foregroundBitmap
	 * @return Bitmap
	 */
	public Bitmap takeOverOverlayBitmap(Bitmap backgroundBitmap, Bitmap foregroundBitmap) {
		 lastID = this.bitmapsWithIntegerID.size()+1;
	    this.bitmapsWithIntegerID.put(lastID, Bitmap.createBitmap(
	    		backgroundBitmap.getWidth(),
	    		backgroundBitmap.getHeight(),
	    		backgroundBitmap.getConfig()));
	    
	    Canvas canvas = new Canvas(this.bitmapsWithIntegerID.get(lastID));
	    
	    canvas.drawBitmap(backgroundBitmap, new Matrix(), null);
	    canvas.drawBitmap(foregroundBitmap, new Matrix(), null);
	    return this.bitmapsWithIntegerID.get(lastID);
	}
	
	/**
	 * Change bitmap size - new Bitmap will be created
	 * @param bitmap - original Bitmap
	 * @param newWidth - new width
	 * @param newHeight - new height
	 * @return resized bitmap
	 */
	public Bitmap takeOverResize(Bitmap bitmap,int newWidth,int newHeight){
		if(bitmap==null)
			return null;
		
    	float w = ((float)newWidth)/bitmap.getWidth();
    	float h = ((float)newHeight)/bitmap.getHeight();
    	Matrix matrix = new Matrix();
    	matrix.postScale(w,h);
    	int ID = this.generateID();
    	this.bitmapsWithIntegerID.put(ID,
    	Bitmap.createBitmap(
    			bitmap,
    			0,
    			0,
    			bitmap.getWidth(),
    			bitmap.getHeight(),
    			matrix,
    			true));
    	return this.bitmapsWithIntegerID.get(ID);
    	
	}
	
	
	private void log(String txt){
		Log.i(TAG, txt);
	}
	private void err(String err){
		Log.e(TAG, err);
	}
}
